﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Growatt.DuiHelpers
{
    /// <summary>
    ///     日志助手类
    /// </summary>
    public static class DuiHelperLog
    {
        #region Fields

        //日志操作锁
        private static readonly object LogSyncLock = new object();

        /// <summary>
        /// 登录或退出的日志文本段
        /// </summary>
        private const string Startup_LOG_INFO = "#################################################### {0} {1} ####################################################";

        /// <summary>
        /// 当前在进行写入操作的日志文件路径
        /// </summary>
        private static string _currentLogPath = string.Format("{0}\\Log\\{1}.log", Directory.GetCurrentDirectory(), DateTime.Now.ToString("yyyy-MM-dd"));

        /// <summary>
        /// 主程序版本号
        /// </summary>
        private static string _programVersion = null;

        /// <summary>
        /// 系统信息
        /// </summary>
        private static string _osInfo = null;

        #endregion Fields

        #region Properties

        /// <summary>
        /// 单个日志文件最大大小，单位为MB。
        /// 默认为5M
        /// </summary>
        public static int LogFileMaxSize { get; set; } = 5;

        /// <summary>
        ///  日志根目录路径
        /// </summary>
        public static string RootLogPath { get; set; }

        #endregion Properties

        #region Functions

        #region Private Function

        /// <summary>
        /// 获取操作系统信息
        /// </summary>
        /// <returns></returns>
        private static string BuildOsInfo()
        {
            StringBuilder result = new StringBuilder();
            result.Append(Environment.NewLine);
            result.Append($"操作系统位数 : {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
            result.Append(Environment.NewLine);
            result.Append($"处理器数 : {Environment.ProcessorCount}");
            result.Append(Environment.NewLine);
            result.Append($"操作系统用户名 : {Environment.UserName}");
            result.Append(Environment.NewLine);
            result.Append($"计算机名 : {Environment.MachineName}");
            result.Append(Environment.NewLine);
            result.Append(Environment.NewLine);
            result.Append($"程序版本号 : {_programVersion}");
            result.Append($"记录时间 : {DateTime.Now}");
            result.AppendLine();
            return result.ToString();
        }

        /// <summary>
        ///  删除一个月前的日志文件
        /// </summary>
        private static void DeleteOldLog()
        {
            if (!Directory.Exists(RootLogPath))
                return;
            try
            {
                DirectoryInfo directoryInfo = new DirectoryInfo(RootLogPath);
                var fileInfos = directoryInfo.GetFiles("*.log", SearchOption.TopDirectoryOnly);
                var oldLog = fileInfos.Where(n => n.CreationTime < DateTime.Now.AddMonths(-1));
                if (oldLog.Any())
                {
                    foreach (var logFile in oldLog)
                    {
                        logFile.Delete();
                    }
                }
            }
            catch (Exception ex)
            {
              //  Debug.WriteLine(ex);
            }
        }

        /// <summary>
        ///  系统启动时，初始化当前日志文件
        /// </summary>
        /// <param name="partNum"></param>
        /// <returns></returns>
        private static string GetLogPath(int partNum)
        {
            string logPath = string.Empty;
            try
            {
                if (!Directory.Exists(RootLogPath))
                {
                    Directory.CreateDirectory(RootLogPath);
                }
                logPath = Path.Combine(RootLogPath, $"{DateTime.Now.ToString("yyyy-MM-dd")}#" + partNum + ".log");
                using (var logFile = File.Open(logPath, FileMode.OpenOrCreate))
                {
                    //超过单个文件大小，则创建当天新日志
                    if (logFile.Length > LogFileMaxSize * 1024 * 1024)
                    {
                        return GetLogPath(++partNum);
                    }
                    //新建时则写入 OS信息,并删除一个月前的日志文件
                    if (logFile.Length == 0)
                    {
                        DeleteOldLog();
                        char[] insertContent = _osInfo.ToCharArray();
                        //转化成 byte[]
                        byte[] byteArrayContent = Encoding.Default.GetBytes(insertContent, 0, insertContent.Length);
                        logFile.Write(byteArrayContent, 0, byteArrayContent.Length);
                    }
                }
            }
            catch (Exception ex)
            {
             //   Debug.WriteLine(ex);
            }
            return logPath;
        }

        ///// <summary>
        /////  写入日志文件
        ///// </summary>
        ///// <param name="logContent"></param>
        //private static void LogToFile(string logContent)
        //{
        //    try
        //    {
        //        lock (LogSyncLock)
        //        {
        //            using (FileStream fs = new FileStream(_currentLogPath, FileMode.Append))
        //            {
        //                using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
        //                {
        //                    sw.WriteLine(logContent);
        //                }
        //            }
        //            Debug.WriteLine(logContent);
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        Debug.WriteLine(ex);
        //    }
        //}

        /// <summary>
        /// 写崩溃日志
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="skipFrames">要跳过的堆栈层级，默认是调用此方法的方法</param>
        private static void WriteFatalLog(string message)
        {
            try
            {
                string fatalPath = Path.Combine(RootLogPath, "Fatal");
                if (!Directory.Exists(fatalPath))
                    Directory.CreateDirectory(fatalPath);

                using (FileStream fs = new FileStream(Path.Combine(fatalPath, DateTime.Now.ToString("yyyy-MM-dd HH_mm_ss") + ".log"), FileMode.Append))
                {
                    using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
                    {
                        sw.WriteLine(message);
                    }
                }
            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.StackTrace);
                Debug.WriteLine(ex);
            }
        }

        #endregion Private Function

        #region Public Function

        public static void StartupLog()
        {
            try
            {
                if (RootLogPath == null)
                    RootLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log");
                _programVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
                _osInfo = BuildOsInfo();
                //初始化日志文件
                _currentLogPath = GetLogPath(0);
            }
            catch (Exception ex)
            {
               // Debug.WriteLine(ex);
            }
        }

        /// <summary>
        /// 写入普通日志
        /// </summary>
        /// <param name="logContent">日志内容</param>
        /// <param name="logType">默认为简单日志</param>
        public static void WriteLog(string logContent, LogType logType = LogType.Simple)
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (logType == LogType.Simple)
            {
                stringBuilder.Append(DateTime.Now);
                stringBuilder.Append("Log : ");
                stringBuilder.Append(logContent ?? "");
            }
            else
            {
                StackTrace stackTrace = new StackTrace(1, true);
                StackFrame stackFrame = stackTrace.GetFrame(0);
                stringBuilder.Append(DateTime.Now);
                stringBuilder.Append("    ");
                stringBuilder.Append(logType.ToString());
                stringBuilder.AppendLine();

                if (logType != LogType.Info)
                {
                    //stringBuilder.Append("Is Main Thread : ");
                    //if (Application.Current != null)
                    //    stringBuilder.Append(Application.Current.Dispatcher.Thread == Thread.CurrentThread);

                    //stringBuilder.Append("     Thread ID : ");
                    //stringBuilder.Append(Thread.CurrentThread.ManagedThreadId);
                    //stringBuilder.AppendLine();

                    stringBuilder.Append("File Path : ");
                    stringBuilder.Append(stackFrame.GetFileName());
                    stringBuilder.AppendLine();

                    stringBuilder.Append("Method Name : ");
                    stringBuilder.Append(stackFrame.GetMethod().Name);
                    stringBuilder.Append("    Code Line : ");
                    stringBuilder.Append(stackFrame.GetFileLineNumber());
                    stringBuilder.AppendLine();
                }

                stringBuilder.Append(" Log: ");
                stringBuilder.Append(logContent ?? "");
                stringBuilder.AppendLine();
            }
            Task.Factory.StartNew(() =>
            {
                DuiHelperFile.AppendFile(stringBuilder.ToString(), _currentLogPath);

                System.IO.FileInfo fileInfo = null;
                try
                {
                    fileInfo = new System.IO.FileInfo(_currentLogPath);
                    if (fileInfo != null && fileInfo.Length > LogFileMaxSize * 1024 * 1024)
                    {
                        _currentLogPath = GetLogPath(0);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    // 其他处理异常的代码
                }
            });
        }

        /// <summary>
        /// 只接受 LogType.Error 和 LogType.Fatal 类型
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="logType"></param>
        /// <param name="skipFrames"></param>
        public static void WriteErrorLog(Exception exp, LogType logType = LogType.Error, int skipFrames = 1)
        {
            StringBuilder stringBuilder = new StringBuilder();
            StackTrace stackTrace = new StackTrace(skipFrames, true);

            StackFrame stackFrame = stackTrace.GetFrame(0);

            if (logType == LogType.Fatal)
            {
                stringBuilder.Append(_osInfo);
            }
            stringBuilder.AppendLine();

            stringBuilder.Append(DateTime.Now);
            stringBuilder.Append("    ");
            stringBuilder.Append(logType.ToString());
            stringBuilder.AppendLine();

            //stringBuilder.Append("Is Main Thread : ");
            //if (Application.Current != null)
            //    stringBuilder.Append(Application.Current.Dispatcher.Thread == Thread.CurrentThread);
            //stringBuilder.AppendLine();
            //stringBuilder.Append("     Thread ID : ");
            //stringBuilder.Append(Thread.CurrentThread.ManagedThreadId);
            //stringBuilder.AppendLine();

            stringBuilder.Append("Error Message : ");
            stringBuilder.Append(exp.Message);
            stringBuilder.AppendLine();

            stringBuilder.Append("Stack Trace : ");
            stringBuilder.Append(exp.StackTrace);
            stringBuilder.AppendLine();
            stringBuilder.AppendLine();

            if (logType == LogType.Fatal)
            {
                WriteFatalLog(stringBuilder.ToString());
                return;
            }

            Task.Factory.StartNew(() =>
            {
                DuiHelperFile.AppendFile(stringBuilder.ToString(), _currentLogPath);
                System.IO.FileInfo fileInfo = null;
                try
                {
                    fileInfo = new System.IO.FileInfo(_currentLogPath);
                    if (fileInfo != null && fileInfo.Length > LogFileMaxSize * 1024 * 1024)
                    {
                        _currentLogPath = GetLogPath(0);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    // 其他处理异常的代码
                }
            });
        }

        #endregion Public Function

        #endregion Functions
    }

    /// <summary>
    /// 日志信息类型
    /// </summary>
    public enum LogType
    {
        [Description("简单信息")]
        Simple,

        [Description("跟踪信息")]
        Trace,

        [Description("行为信息")]
        Info,

        [Description("警告信息")]
        Warnning,

        [Description("错误信息")]
        Error,

        [Description("系统无法继续运行")]
        Fatal
    }
}